<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8"/>
        <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
        <title>AJ Framework-Bean 实体校验</title>
        <meta name="description" content="AJ Framework 一个基于 SpringMVC 构建的轻量级框架，旨在增强 SpringMVC 并使其更具 SpringBoot 的特性。它拥有许多小型组件，非常易于使用。" />
        <meta name="keywords" content="AJ Framework, ajaxjs, ajaxjs framework, java framework, web framework" />
        <meta name="viewport" content="width=device-width, initial-scale=1"/>
        <link rel="preconnect" href="https://fonts.googleapis.com" />
        <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
        <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@200..900&family=Noto+Serif:ital,wght@0,100..900;1,100..900&display=swap&family=Noto+Sans+SC:wght@100..900&display=swap" />
        <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Noto+Sans+SC:wght@100..900&family=Noto+Serif:ital,wght@0,100..900;1,100..900&display=swap" /> 
        <link rel="stylesheet" href="https://framework.ajaxjs.com/static/new-ui/css/common.css" />
        <link rel="stylesheet" href="https://iam.ajaxjs.com/asset/main.css"/>
        <link rel="icon" type="image/x-icon" href="https://framework.ajaxjs.com/aj-logo/logo.ico"/>
        <script src="https://framework.ajaxjs.com/static/aj-docs/common.js"></script>
        <script>
            var _hmt = _hmt || [];
            (function() {
              var hm = document.createElement("script");
              hm.src = "https://hm.baidu.com/hm.js?a3dec1d9b9415edd86b54c1fb3d02f19";
              var s = document.getElementsByTagName("script")[0];
              s.parentNode.insertBefore(hm, s);
            })();
        </script>
    </head>
    <body>
        <nav>
            <div>
                <div class="links">
                    <a href="/">🏠 首页</a>
                    | ⚙️ 源码:
                    <a target="_blank" href="https://github.com/lightweight-component/aj-framework">Github</a>/<a target="_blank" href="https://gitcode.com/lightweight-component/aj-framework">Gitcode</a>
                    |
                    <a href="/common/contact">✉️ 联系</a>
                </div>
                <h1>
                    <img src="https://framework.ajaxjs.com/aj-logo/logo.png" style="vertical-align: middle;height: 45px;margin-bottom: 6px;" />
                    AJ Framework
                </h1>
                <h3>轻量级 Java 快速开发框架
                </h3>
            </div>
        </nav>
        <div>
            <menu>
                
                <ul>
                    <li class="selected">
                        <a href="/">首页</a>
                    </li>
                </ul>
                <h3>框架基本用法</h3>
                <ul>
                    <li>
                        <a href="/framework/init">初始化一个项目</a>
                    </li>
                    <li>
                        <a href="/framework/controller">简化控制器为接口</a>
                    </li>
                    <li>
                        <a href="/framework/unified-return">统一对象返回</a>
                    </li>
                    <li>
                        <a href="/framework/unified-ex">统一异常处理</a>
                    </li>
                    <li>
                        <a href="/framework/package">打包与部署</a>
                    </li>
                </ul>
                <h3>常用组件</h3>
                <ul>
                    <li>
                        <a href="/component/db">数据库访问</a>
                    </li>
                    <li>
                        <a href="/component/bean-validator/">Bean 实体校验</a>
                    </li>
                    <li>
                        <a href="/component/security">安全组件、用户系统</a>
                    </li>
                    <li>
                        <a href="/component/lite-c/">使用简单本地缓存</a>
                    </li>
                  <li>
                       <a href="/component/cache/">使用多级缓存</a>
                   </li>
                </ul>

                <h3>其他</h3>
                <ul>
                    <li><a href="/common/contact">联系</a></li>
                </ul>
            </menu>
            <article class="aj-text chinese">
                <h1>Bean 实体校验</h1>
<p>对于前端的入参，无论是查询的参数还是写入的数据，都需要对其进行合法性的校验，避免数据错误，过滤一些明显不合法的请求。</p>
<p>在 Spring 体系中存在这两种校验机制：</p>
<ul>
<li>基于 JSR-303 标准的实体校验，注解是<code>@Valid</code>，有下面两种实现
<ul>
<li>Hibernate Validator，最常见的实现，但是笨重，可达 13mb 的 JAR 包</li>
<li>Apache BVal，比较小众的实现，但轻量级得多，我之前也使用，详见<a href="https://blog.csdn.net/zhangxin09/article/details/50600575">文章</a></li>
</ul>
</li>
<li>Spring 自带的校验器 Validator，注解是<code>@Validated</code></li>
</ul>
<p>可见 Spring 的更简单和轻量级，但是默认不支持 JSR 注解。怎么结合 JSR-303 注解发挥其作用呢？这正是文本所介绍的组件要解决的问题。 这个组件三个核心类 <code>ValidatorInitializing</code>、<code>ValidatorImpl</code>、<code>ValidatorEnum</code>，去掉注释后总共不超过 200 行源码，实现 10 多 MB 的 Hibernate Validator 的多数功能。</p>
<div class="ref">
    <span class="c">javax.validation</span> 2.0 是 JSR 380 的版本。JSR 380 是 Java 规范请求的缩写，它定义了 Java Bean 验证 API（Java Bean Validation API）。Java Bean 验证 API 提供了一组用于验证对象属性的注解和接口，帮助开发人员进行数据验证和约束。
</div>
<p>组件源码：<a href="https://gitcode.com/lightweight-component/aj-framework/tree/master/aj-framework/src/main/java/com/ajaxjs/framework/validator">https://gitcode.com/lightweight-component/aj-framework/tree/master/aj-framework/src/main/java/com/ajaxjs/framework/validator</a>。</p>
<h2>配置方式</h2>
<p>首先要在 YAML 配置文件中增加默认的出错提示信息。</p>
<pre><code class="language-yaml">javax-validation:
  javax.validation.constraints.AssertTrue.message: 值必须为 true
  javax.validation.constraints.AssertFalse.message: 值必须为 false
  javax.validation.constraints.DecimalMax.message: 值不能大于 {value}
  javax.validation.constraints.DecimalMin.message: 值不能小于 {value}
  javax.validation.constraints.Digits.message: 数字值超出范围（应为 [{integer} digits].[{fraction} digits]）
  javax.validation.constraints.Email.message: 值必须为有效的电子邮箱地址
  javax.validation.constraints.Future.message: 值必须为将来的日期
  javax.validation.constraints.FutureOrPresent.message: 值必须为当前或将来的日期
  javax.validation.constraints.Max.message: 值不能大于 {value}
  javax.validation.constraints.Min.message: 值不能小于 {value}
  javax.validation.constraints.Negative.message: 值必须为负数
  javax.validation.constraints.NegativeOrZero.message: 值必须为非正数
  javax.validation.constraints.NotBlank.message: 值不能为空值或空白字符串
  javax.validation.constraints.NotEmpty.message: 值不能为空值、null 或空集合
  javax.validation.constraints.NotNull.message: 值不能为空
  javax.validation.constraints.Null.message: 值必须为空
  javax.validation.constraints.Past.message: 值必须为过去的日期
  javax.validation.constraints.PastOrPresent.message: 值必须为当前或过去的日期
  javax.validation.constraints.Positive.message: 值必须为正数
  javax.validation.constraints.PositiveOrZero.message: 值必须为非负数
  javax.validation.constraints.Pattern.message: 值必须与指定正则表达式匹配
  javax.validation.constraints.Size.message: 大小必须小于 {max}，大于 {min}
</code></pre>
<h3>初始化校验组件</h3>
<p>引入 aj-framework 即可自动装配该组件。如果单独使用请注入 <code>ValidatorInitializing</code>。这是在 Spring 应用程序上下文初始化完成后设置验证器和参数解析器。这个类的作用是在 Spring 启动时，拦截并修改 <code>RequestMappingHandlerAdapter</code> 的行为。通过设置自定义的验证器和参数解析器，可以对路径变量进行验证。</p>
<pre><code class="language-java">@Bean
public ValidatorInitializing initValidator() {
    return new ValidatorInitializing();
}
</code></pre>
<h2>使用方法</h2>
<p>首先在参数实体属性上添加对应的注解。</p>
<pre><code class="language-java">import javax.validation.constraints.NotNull;

@Data
public class JvmInfo implements IBaseModel {
    private String name;

    @NotNull
    private String classPath;
    
    // ……
}
</code></pre>
<p>然后在 controller 里面方法参数上添加 <code>@Validated</code> 注解，注意是 <code>org.springframework.validation.annotation.Validated</code>。</p>
<pre><code class="language-java">@PostMapping(&quot;/test&quot;)
public boolean test(@Validated JvmInfo info) {
    System.out.println(info);
    return true;
}
</code></pre>
<h3>路径参数的校验</h3>
<p>这是基于 POST 方法提交实体的校验，那么对于路径上的参数是否支持校验呢？答案是支持的。</p>
<p>在 controller 里面方法参数上直接添加你要校验的注解：</p>
<pre><code class="language-java">@RequestMapping(&quot;/test/{mobileNo}/{idNo}&quot;)
public Map&lt;String, Object&gt; test(@PathVariable @MobileNo String mobileNo, @PathVariable @IdCard String idNo) { 
    // ……
}
</code></pre>
<p>便可完成对路径参数的校验了。一般来说既然是路径的参数，那么就是必填非空的了。</p>
<p>值得注意的是，这里的 <code>@MobileNo</code>、<code>@IdCard</code> 都是自定义的注解，而非标准的 JSR 380 所提供的。这里顺便说说自定义的校验注解的写法。</p>
<h3>自定义的校验注解</h3>
<p>首先定义注解：</p>
<pre><code class="language-java">import java.lang.annotation.*;

@Documented
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
public @interface IdCard {
    String message() default &quot;身份证号格式不正确&quot;;

    boolean required() default true;
}
</code></pre>
<p>然后在枚举类 <code>ValidatorEnum</code> 中增加具体的校验方法，如果不通过就抛出 <code>ValidatorException</code> 异常。</p>
<p><img src="../../../imgs/bean-v.png" alt="Bean Validation"></p>
<p>至此就完成了自定义注解的定义。</p>
<h2>原理分析</h2>
<p>有关原理的分析，请移步至博客文章：↗ <a href="https://zhangxin.blog.csdn.net/article/details/132255031">CSDN 博客文章</a>。</p>

            </article>
        </div>
        <footer>
             开源框架 <a href="https://framework.ajaxjs.com" target="_blank">AJ-Framework</a> 首页。联系方式：
             frank@ajaxjs.com，<a href="https://blog.csdn.net/zhangxin09" target="_blank">作者博客</a>
             <br />
             <br />
             Copyright © 2025 Frank Cheung. All rights reserved.
         </footer>
    </body>
</html>